package com.hmy.infrastructure.service;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.profile.DefaultProfile;
import com.hmy.common.exception.ClientException;
import com.hmy.common.json.CcpJacksonkMapper;
import com.hmy.infrastructure.api.bo.request.sms.CheckVerificationCodeRequest;
import com.hmy.infrastructure.api.bo.request.sms.SendVerificationCodeRequest;
import com.hmy.infrastructure.api.bo.request.sms.SmsRequest;
import com.hmy.infrastructure.bo.response.CheckVerificationCodeResponse;
import com.hmy.infrastructure.common.properties.SmsProperties;
import com.hmy.infrastructure.dal.dto.SendVerificationCodeDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * 短信服务
 *
 * @author xiongwei
 * @description: TODO
 * @date 2023/11/13
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SmsService {
    /**
     * 短信发送成功
     */
    private static final String SUCCESS = "OK";
    /**
     * 字母随机基础字符串
     */
    private static final String LETTER_BASE = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private final SmsProperties smsProperties;
    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 发送短信
     *
     * @param smsRequest 参数
     */
    public void sendSms(SmsRequest smsRequest) {

        SendSmsRequest request = new SendSmsRequest();
        request.setSignName(smsProperties.getDefaultSignName());
        request.setTemplateCode(smsRequest.getTemplateCode());
        request.setPhoneNumbers(smsRequest.getPhoneNumbers());
        request.setTemplateParam(CcpJacksonkMapper.convertObjectToString(smsRequest.getTemplateParam()));
        request.setSmsUpExtendCode(null);
        request.setOutId(null);


        send(request);


    }

    /**
     * 获取短信配置
     *
     * @return SmsProperties
     */
    public SmsProperties getSmsProperties() {
        return smsProperties;
    }

    /**
     * 发送短信
     *
     * @param request 短信
     */
    private void send(SendSmsRequest request) {
        try {
            DefaultProfile profile = DefaultProfile.getProfile(smsProperties.getRegionId(), smsProperties.getAccessKeyId(), smsProperties.getAccessKeySecret());
            IAcsClient client = new DefaultAcsClient(profile);
            log.info("发送短信请求：{}", CcpJacksonkMapper.convertObjectToString(request));
            SendSmsResponse response = client.getAcsResponse(request);
            log.info("发送短信响应：{}", CcpJacksonkMapper.convertObjectToString(response));
            // 失败
            if (!SUCCESS.equalsIgnoreCase(response.getCode())) {
                throw new ClientException("发送短信失败: " + response.getMessage());
            }
        } catch (ClientException e) {
            throw new ClientException("发送短信失败: " + e.getMessage());
        } catch (ServerException e) {
            throw new ClientException("发送短信失败: " + e.getMessage());
        } catch (com.aliyuncs.exceptions.ClientException e) {
            throw new ClientException("发送短信失败: " + e.getMessage());
        }
    }

    /**
     * 发送验证码
     *
     * @param sendVerificationCodeRequest 请求参数
     * @return 发送结果
     */
    public boolean sendVerificationCode(SendVerificationCodeRequest sendVerificationCodeRequest) {
        SendVerificationCodeDTO dto = SendVerificationCodeDTO.buildDto(sendVerificationCodeRequest);
        Assert.notEmpty(smsProperties.getCodeList(), "验证码配置不存在");
        Optional<SmsProperties.VerificationCode> verificationCode = smsProperties.getCodeList().stream().filter(i -> i.getBusinessKey().equals(dto.getBusinessKey())).findFirst();
        verificationCode.ifPresentOrElse(dto::setVerificationCode, () -> {
            throw new ClientException("验证码配置不存在");
        });
        SendSmsRequest request = new SendSmsRequest();
        request.setSignName(smsProperties.getDefaultSignName());
        request.setTemplateCode(dto.getVerificationCode().getTemplateCode());
        request.setPhoneNumbers(dto.getPhone());
        String code = "";
        switch (dto.getVerificationCode().getType()) {
            case NUMBER: {
                code = RandomUtil.randomNumbers(dto.getVerificationCode().getLength());
                break;
            }
            case LETTER: {
                code = RandomUtil.randomString(LETTER_BASE, dto.getVerificationCode().getLength());
                break;
            }
            case MIXED: {
                code = RandomUtil.randomString(dto.getVerificationCode().getLength());
                break;
            }
        }
        Assert.notEmpty(code, "验证码发送失败");
        Map<String, String> map = new HashMap<>();
        map.put(dto.getVerificationCode().getCodeKey(), code);
        request.setTemplateParam(CcpJacksonkMapper.convertObjectToString(map));
        request.setSmsUpExtendCode(null);
        request.setOutId(null);
        send(request);
        this.redisTemplate.opsForValue().set(dto.getCacheCodeKey(), code, dto.getVerificationCode().getValidTime(), TimeUnit.SECONDS);
        return true;
    }

    public CheckVerificationCodeResponse checkVerificationCode(CheckVerificationCodeRequest checkVerificationCodeRequest) {
        //遍历defaultCodePhoneList，判断checkVerificationCodeRequest.getCacheCodeKey()是不是以defaultCodePhoneList中的某个元素结尾
        //如果是，返回CheckVerificationCodeResponse.success()
        for (String phone : smsProperties.getDefaultCodePhoneList()) {
            if (checkVerificationCodeRequest.getCacheCodeKey().endsWith(phone)) {
                return CheckVerificationCodeResponse.success();
            }
        }
        if (this.redisTemplate.hasKey(checkVerificationCodeRequest.getCacheCodeKey())) {
            if (this.redisTemplate.opsForValue().get(checkVerificationCodeRequest.getCacheCodeKey()).equals(checkVerificationCodeRequest.getCode())) {
                this.redisTemplate.delete(checkVerificationCodeRequest.getCacheCodeKey());
                return CheckVerificationCodeResponse.success();
            }
            return CheckVerificationCodeResponse.fail("验证码已过期");
        }
        return CheckVerificationCodeResponse.fail("验证码已过期");
    }
}
